#include "StdAfx.h"
#include "TdBulkCopy.h"

#include "connection.h"
#include "schema.h"
#include "DMLGroup.h"

using namespace System::Collections::Generic;
using namespace System::Net;
using namespace System::Net::Sockets;
using namespace System::Text;
using namespace Teradata::Client::Provider;
using namespace teradata::client::API;
namespace TPT = TdParallelTransportApi;

TPT::TdBulkCopy::TdBulkCopy(TdBulkConnection^ connection) :
	m_Connection(connection), m_LogLevel(TPT::TdLogLevel::Off), m_NotifyAfter(0), m_DestinationTableName(nullptr)
{
}

int
TPT::TdBulkCopy::NotifyAfter::get()
{
	return m_NotifyAfter;
}

void
TPT::TdBulkCopy::NotifyAfter::set(int value)
{
	m_NotifyAfter = value;
}

System::String^
TPT::TdBulkCopy::DestinationTableName::get()
{
	return m_DestinationTableName;
}

void
TPT::TdBulkCopy::DestinationTableName::set(System::String^ value)
{
	m_DestinationTableName = value;
}

TPT::TdLogLevel
TPT::TdBulkCopy::LogLevel::get()
{
	return m_LogLevel;
}

void
TPT::TdBulkCopy::LogLevel::set(TPT::TdLogLevel value)
{
	m_LogLevel = value;
}

void SetBit(array<unsigned char>^ data, int index)
{
	int offset = (index / 8);
	int bit = 7 - (index % 8);

	data[offset] = data[offset] | (1 << bit);
}

char* UnMarshalArray(array<unsigned char>^ buffer, int len)
{
	char* ptr = new char[len];

	for (int i = 0; i < len; i++)
	{
		ptr[i] = (char)buffer[i];
	}

	return ptr;
}

void BCCheckStatus(teradata::client::API::Connection *conn, TD_StatusCode status)
{
	if (status >= TD_Error)
	{
		char* msg;
		TD_ErrorType type;
		conn->GetErrorInfo(&msg, &type);
		throw gcnew System::InvalidOperationException("Teradata Error (" + ((int)type).ToString() + "): " + gcnew System::String(msg));
	}
}

void
TPT::TdBulkCopy::Initialize(array<TdColumn^>^ sourceColumns, int restartmode, void** conn_ptr, void** dmlGr_ptr, void** schema_ptr)
{
	this->username = (char*)(void*)Marshal::StringToHGlobalAnsi(this->m_Connection->Username);
	this->password = (char*)(void*)Marshal::SecureStringToGlobalAllocAnsi(this->m_Connection->Password);

	array<IPAddress^>^ entry = Dns::GetHostAddresses(this->m_Connection->Server);
	System::String^ serverIP;
	for (int i = 0; i < entry->Length; i++)
	{
		if (entry[i]->AddressFamily == Sockets::AddressFamily::InterNetwork)
		{
			serverIP = entry[i]->ToString();
			break;
		}
	}

	if (System::String::IsNullOrEmpty(serverIP))
	{
		throw gcnew System::InvalidOperationException("The Server Name Was Not Found.");
	}

	this->server = (char*)(void*)Marshal::StringToHGlobalAnsi(serverIP);
	this->database = (char*)(void*)Marshal::StringToHGlobalAnsi(this->m_Connection->Database);
	this->target = (char*)(void*)Marshal::StringToHGlobalAnsi(this->m_DestinationTableName);
	this->log = (char*)(void*)Marshal::StringToHGlobalAnsi(this->m_Connection->Database + "." + this->m_DestinationTableName + "_LOG");

	Connection *conn = new Connection(TD_UTF8_ENCODING);

	/**********************************************
	* Add Attributes
	**********************************************/
	conn->AddAttribute(TD_SYSTEM_OPERATOR, TD_LOAD);

	conn->AddAttribute(TD_USER_NAME, this->username);
	conn->AddAttribute(TD_USER_PASSWORD, this->password);
	conn->AddAttribute(TD_TDP_ID, this->server);
	conn->AddAttribute(TD_WORKINGDATABASE, this->database);
	conn->AddAttribute(TD_TARGET_TABLE, this->target);
	conn->AddAttribute(TD_LOG_TABLE, this->log);
	conn->AddAttribute(TD_ROBUST, "No");
	conn->AddAttribute(TD_MIN_SESSIONS, 1);
	conn->AddAttribute(TD_MAX_SESSIONS, 4);
	conn->AddAttribute(TD_INSTANCE_NUM, 1);
	conn->AddAttribute(TD_CHARSET, "UTF16");

	if (restartmode == 1)
	{
		conn->AddAttribute(TD_RESTARTMODE, 1);
	}

	this->logLevel = NULL;

	if (this->m_LogLevel != TdLogLevel::Off)
	{
		this->logLevel = GetLogLevelString(this->m_LogLevel);
		conn->AddAttribute(TD_NOTIFY_LEVEL, this->logLevel);
		conn->AddAttribute(TD_NOTIFY_METHOD, "Msg");
	}

	/*if (this->m_BatchSize > 1)
	{
		conn->AddAttribute(TD_BUFFER_MODE, "Yes");
	}*/

	/**********************************************
	* Add Schema
	**********************************************/
	StringBuilder^ sb = gcnew StringBuilder();
	sb->Append("INSERT INTO ");
	sb->Append(this->m_DestinationTableName);
	sb->Append("( ");

	System::Collections::IEnumerator^ colEnum = sourceColumns->GetEnumerator();

	this->fieldList = new char*[TdBulkCopy::MaxColumns];
	int fcount = 0;
	bool first = true;

	Schema *schema = new Schema("input");
	while (colEnum->MoveNext())
	{
		TdColumn^ col = (TdColumn^)colEnum->Current;
		if (first)
		{
			sb->Append(":");
			first = false;
		}
		else
		{
			sb->Append(", :");
		}

		sb->Append(col->ColumnName);

		this->fieldList[fcount] = (char*)(void*)Marshal::StringToHGlobalAnsi(col->ColumnName);
		schema->AddColumn(fieldList[fcount], TdColumn::GetTdDataType(col->DataType), col->Size, col->Precision, col->Scale);

		fcount++;
	}

	for (int i = fcount; i < TdBulkCopy::MaxColumns; i++)
	{
		this->fieldList[i] = NULL;
	}

	sb->Append(");");
	
	conn->AddSchema(schema);

	/**********************************************
	* Add DMLGroups
	**********************************************/
	TD_Index dmlGroupIndex = 0;
	DMLGroup * dmlGr = new DMLGroup();

	this->insertStatement = (char*)(void*)Marshal::StringToHGlobalAnsi(sb->ToString());
	dmlGr->AddStatement(this->insertStatement);

	dmlGr->AddSerializeOn(fcount, fieldList[0], fieldList[1], fieldList[2], fieldList[3], fieldList[4], fieldList[5], fieldList[6], fieldList[7], fieldList[8], fieldList[9], fieldList[10], fieldList[11], fieldList[12], fieldList[13], fieldList[14], fieldList[15], fieldList[16], fieldList[17], fieldList[18], fieldList[19], fieldList[20], fieldList[21], fieldList[22], fieldList[23], fieldList[24], fieldList[25], fieldList[26], fieldList[27], fieldList[28], fieldList[29], fieldList[30], fieldList[31], fieldList[32], fieldList[33], fieldList[34], fieldList[35], fieldList[36], fieldList[37], fieldList[38], fieldList[39], fieldList[40], fieldList[41], fieldList[42], fieldList[43], fieldList[44], fieldList[45], fieldList[46], fieldList[47], fieldList[48], fieldList[49], fieldList[50], fieldList[51], fieldList[52], fieldList[53], fieldList[54], fieldList[55], fieldList[56], fieldList[57], fieldList[58], fieldList[59], fieldList[60], fieldList[61], fieldList[62], fieldList[63], fieldList[64], fieldList[65], fieldList[66], fieldList[67], fieldList[68], fieldList[69], fieldList[70], fieldList[71], fieldList[72], fieldList[73], fieldList[74], fieldList[75], fieldList[76], fieldList[77], fieldList[78], fieldList[79], fieldList[80], fieldList[81], fieldList[82], fieldList[83], fieldList[84], fieldList[85], fieldList[86], fieldList[87], fieldList[88], fieldList[89], fieldList[90], fieldList[91], fieldList[92], fieldList[93], fieldList[94], fieldList[95], fieldList[96], fieldList[97], fieldList[98], fieldList[99], fieldList[100], fieldList[101], fieldList[102], fieldList[103], fieldList[104], fieldList[105], fieldList[106], fieldList[107], fieldList[108], fieldList[109], fieldList[110], fieldList[111], fieldList[112], fieldList[113], fieldList[114], fieldList[115], fieldList[116], fieldList[117], fieldList[118], fieldList[119], fieldList[120], fieldList[121], fieldList[122], fieldList[123], fieldList[124], fieldList[125], fieldList[126], fieldList[127], fieldList[128], fieldList[129], fieldList[130], fieldList[131], fieldList[132], fieldList[133], fieldList[134], fieldList[135], fieldList[136], fieldList[137], fieldList[138], fieldList[139], fieldList[140], fieldList[141], fieldList[142], fieldList[143], fieldList[144], fieldList[145], fieldList[146], fieldList[147], fieldList[148], fieldList[149], fieldList[150], fieldList[151], fieldList[152], fieldList[153], fieldList[154], fieldList[155], fieldList[156], fieldList[157], fieldList[158], fieldList[159], fieldList[160], fieldList[161], fieldList[162], fieldList[163], fieldList[164], fieldList[165], fieldList[166], fieldList[167], fieldList[168], fieldList[169], fieldList[170], fieldList[171], fieldList[172], fieldList[173], fieldList[174], fieldList[175], fieldList[176], fieldList[177], fieldList[178], fieldList[179], fieldList[180], fieldList[181], fieldList[182], fieldList[183], fieldList[184], fieldList[185], fieldList[186], fieldList[187], fieldList[188], fieldList[189], fieldList[190], fieldList[191], fieldList[192], fieldList[193], fieldList[194], fieldList[195], fieldList[196], fieldList[197], fieldList[198], fieldList[199], fieldList[200], fieldList[201], fieldList[202], fieldList[203], fieldList[204], fieldList[205], fieldList[206], fieldList[207], fieldList[208], fieldList[209], fieldList[210], fieldList[211], fieldList[212], fieldList[213], fieldList[214], fieldList[215], fieldList[216], fieldList[217], fieldList[218], fieldList[219], fieldList[220], fieldList[221], fieldList[222], fieldList[223], fieldList[224], fieldList[225], fieldList[226], fieldList[227], fieldList[228], fieldList[229], fieldList[230], fieldList[231], fieldList[232], fieldList[233], fieldList[234], fieldList[235], fieldList[236], fieldList[237], fieldList[238], fieldList[239], fieldList[240], fieldList[241], fieldList[242], fieldList[243], fieldList[244], fieldList[245], fieldList[246], fieldList[247], fieldList[248], fieldList[249], fieldList[250], fieldList[251], fieldList[252], fieldList[253], fieldList[254], fieldList[255]);
	dmlGr->AddUseList(fcount, fieldList[0], fieldList[1], fieldList[2], fieldList[3], fieldList[4], fieldList[5], fieldList[6], fieldList[7], fieldList[8], fieldList[9], fieldList[10], fieldList[11], fieldList[12], fieldList[13], fieldList[14], fieldList[15], fieldList[16], fieldList[17], fieldList[18], fieldList[19], fieldList[20], fieldList[21], fieldList[22], fieldList[23], fieldList[24], fieldList[25], fieldList[26], fieldList[27], fieldList[28], fieldList[29], fieldList[30], fieldList[31], fieldList[32], fieldList[33], fieldList[34], fieldList[35], fieldList[36], fieldList[37], fieldList[38], fieldList[39], fieldList[40], fieldList[41], fieldList[42], fieldList[43], fieldList[44], fieldList[45], fieldList[46], fieldList[47], fieldList[48], fieldList[49], fieldList[50], fieldList[51], fieldList[52], fieldList[53], fieldList[54], fieldList[55], fieldList[56], fieldList[57], fieldList[58], fieldList[59], fieldList[60], fieldList[61], fieldList[62], fieldList[63], fieldList[64], fieldList[65], fieldList[66], fieldList[67], fieldList[68], fieldList[69], fieldList[70], fieldList[71], fieldList[72], fieldList[73], fieldList[74], fieldList[75], fieldList[76], fieldList[77], fieldList[78], fieldList[79], fieldList[80], fieldList[81], fieldList[82], fieldList[83], fieldList[84], fieldList[85], fieldList[86], fieldList[87], fieldList[88], fieldList[89], fieldList[90], fieldList[91], fieldList[92], fieldList[93], fieldList[94], fieldList[95], fieldList[96], fieldList[97], fieldList[98], fieldList[99], fieldList[100], fieldList[101], fieldList[102], fieldList[103], fieldList[104], fieldList[105], fieldList[106], fieldList[107], fieldList[108], fieldList[109], fieldList[110], fieldList[111], fieldList[112], fieldList[113], fieldList[114], fieldList[115], fieldList[116], fieldList[117], fieldList[118], fieldList[119], fieldList[120], fieldList[121], fieldList[122], fieldList[123], fieldList[124], fieldList[125], fieldList[126], fieldList[127], fieldList[128], fieldList[129], fieldList[130], fieldList[131], fieldList[132], fieldList[133], fieldList[134], fieldList[135], fieldList[136], fieldList[137], fieldList[138], fieldList[139], fieldList[140], fieldList[141], fieldList[142], fieldList[143], fieldList[144], fieldList[145], fieldList[146], fieldList[147], fieldList[148], fieldList[149], fieldList[150], fieldList[151], fieldList[152], fieldList[153], fieldList[154], fieldList[155], fieldList[156], fieldList[157], fieldList[158], fieldList[159], fieldList[160], fieldList[161], fieldList[162], fieldList[163], fieldList[164], fieldList[165], fieldList[166], fieldList[167], fieldList[168], fieldList[169], fieldList[170], fieldList[171], fieldList[172], fieldList[173], fieldList[174], fieldList[175], fieldList[176], fieldList[177], fieldList[178], fieldList[179], fieldList[180], fieldList[181], fieldList[182], fieldList[183], fieldList[184], fieldList[185], fieldList[186], fieldList[187], fieldList[188], fieldList[189], fieldList[190], fieldList[191], fieldList[192], fieldList[193], fieldList[194], fieldList[195], fieldList[196], fieldList[197], fieldList[198], fieldList[199], fieldList[200], fieldList[201], fieldList[202], fieldList[203], fieldList[204], fieldList[205], fieldList[206], fieldList[207], fieldList[208], fieldList[209], fieldList[210], fieldList[211], fieldList[212], fieldList[213], fieldList[214], fieldList[215], fieldList[216], fieldList[217], fieldList[218], fieldList[219], fieldList[220], fieldList[221], fieldList[222], fieldList[223], fieldList[224], fieldList[225], fieldList[226], fieldList[227], fieldList[228], fieldList[229], fieldList[230], fieldList[231], fieldList[232], fieldList[233], fieldList[234], fieldList[235], fieldList[236], fieldList[237], fieldList[238], fieldList[239], fieldList[240], fieldList[241], fieldList[242], fieldList[243], fieldList[244], fieldList[245], fieldList[246], fieldList[247], fieldList[248], fieldList[249], fieldList[250], fieldList[251], fieldList[252], fieldList[253], fieldList[254], fieldList[255]);

	BCCheckStatus(conn, conn->AddDMLGroup(dmlGr,&dmlGroupIndex));

	BCCheckStatus(conn, conn->Initiate());

	*conn_ptr = (void*)conn;
	*dmlGr_ptr = (void*)dmlGr;
	*schema_ptr = (void*)schema;
}

void
TPT::TdBulkCopy::TerminateExistingSession(array<TdColumn^>^ sourceColumns)
{
	Connection* conn;
	DMLGroup* dmlGr;
	Schema* schema;

	this->Initialize(sourceColumns, 1, (void**)&conn, (void**)&dmlGr, (void**)&schema);

	try
	{
		BCCheckStatus(conn, conn->EndAcquisition());
	}
	catch (System::Exception^)
	{
		throw;
	}
	finally
	{
		try
		{
			conn->Terminate();
		}
		catch (System::Exception^)
		{
		}

		delete dmlGr;
		delete schema;
		delete conn;

		this->CleanUp();
	}
}

void
TPT::TdBulkCopy::WriteToServer(array<TdColumn^>^ sourceColumns,
							  IEnumerator<array<Object^>^>^ sourceData)
{
	Connection* conn;
	DMLGroup* dmlGr;
	Schema* schema;

	this->Initialize(sourceColumns, 1, (void**)&conn, (void**)&dmlGr, (void**)&schema);

	long copied = 0;

	try
	{
		while (sourceData->MoveNext())
		{
			array<System::Object^>^ currentRow = sourceData->Current;
			int dataLen = 0;

			for (int i = 0; i < sourceColumns->Length; i++)
			{
				System::Object^ currentObj = currentRow[i];
				switch (sourceColumns[i]->DataType)
				{
				case TdType::VarByte:
					if (currentObj != nullptr)
					{
						dataLen += 2 + ((array<System::Byte>^)currentObj)->Length;
					}
					else
					{
						dataLen += 2;
					}

					break;
				case TdType::VarChar:
					if (currentObj != nullptr)
					{
						System::String^ value;

						if (currentObj->GetType() == array<System::Char>::typeid)
						{
							value = gcnew System::String((array<System::Char>^)currentObj);
						}
						else if (currentObj->GetType() == System::String::typeid)
						{
							value = (System::String^)currentObj;
						}
						else if (currentObj->GetType() == System::Char::typeid)
						{
							value = gcnew System::String(gcnew array<System::Char> { (System::Char)currentObj });
						}
						else
						{
							throw gcnew System::InvalidOperationException("Type Mismatch");
						}

						if (value->Length > sourceColumns[i]->Length)
						{
							value = value->Substring(0, sourceColumns[i]->Length);
						}

						dataLen += 2 + Encoding::Unicode->GetByteCount(value);
					}
					else
					{
						dataLen += 2;
					}

					break;
				default:
					dataLen += sourceColumns[i]->Size;
					break;
				}
			}

			int rowLen = (sourceColumns->Length + 7) / 8 + dataLen;
			array<unsigned char>^ buffer = gcnew array<unsigned char>(rowLen);

			int offset = 0;

			int nullBytes = (sourceColumns->Length + 7) / 8;
			for (; offset < nullBytes; offset++)
			{
				buffer[offset] = 0;
			}

			for (int i = 0; i < sourceColumns->Length; i++)
			{
				System::Object^ currentObj = currentRow[i];

				if (currentObj == nullptr)
				{
					SetBit(buffer, i);

					if (sourceColumns[i]->DataType == TdType::Char)
					{
						array<System::Byte>^ bytes = Encoding::Unicode->GetBytes(gcnew System::String(' ', sourceColumns[i]->Length));

						for (int j = 0; j < bytes->Length; j++)
						{
							buffer[offset + j] = bytes[j];
						}

						offset += bytes->Length;
					}
					else if (sourceColumns[i]->DataType == TdType::VarByte || sourceColumns[i]->DataType == TdType::VarChar)
					{
						// Write zero Length
						buffer[offset++] = 0;
						buffer[offset++] = 0;
					}
					else
					{
						for (int j = 0; j < sourceColumns[i]->Size; j++)
						{
							buffer[offset + j] = 0;
						}

						offset += sourceColumns[i]->Size;
					}
				}
				else
				{
					switch (sourceColumns[i]->DataType)
					{
					case TdType::Byte:
						if (sourceColumns[i]->Length == 1)
						{
							buffer[offset] = (unsigned char)currentObj;
						}
						else
						{
							array<System::Byte>^ value = (array<System::Byte>^)currentObj;

							System::Array::Copy(value, 0, buffer, offset, min(sourceColumns[i]->Length, value->Length));

							for (int j = value->Length; j < sourceColumns[i]->Length; j++)
							{
								buffer[offset + j] = 0;
							}
						}

						offset += sourceColumns[i]->Length;

						break;
					case TdType::VarByte:
						{
							array<System::Byte>^ value = (array<System::Byte>^)currentObj;
							
							System::Array::Copy(System::BitConverter::GetBytes((System::UInt16)value->Length), 0, buffer, offset, 2);
							offset += 2;

							System::Array::Copy(value, 0, buffer, offset, min(sourceColumns[i]->Length, value->Length));
							offset += value->Length;
						}

						break;
					case TdType::Char:
						{
							System::String^ value;

							if (currentObj->GetType() == array<System::Char>::typeid)
							{
								value = gcnew System::String((array<System::Char>^)currentObj);
							}
							else if (currentObj->GetType() == System::String::typeid)
							{
								value = (System::String^)currentObj;
							}
							else if (currentObj->GetType() == System::Char::typeid)
							{
								value = gcnew System::String(gcnew array<System::Char> { (System::Char)currentObj });
							}
							else
							{
								value = currentObj->ToString();
							}

							if (value->Length > sourceColumns[i]->Length)
							{
								value = value->Substring(0, sourceColumns[i]->Length);
							}
							else if (value->Length < sourceColumns[i]->Length)
							{
								value = value->PadRight(sourceColumns[i]->Length, ' ');
							}

							array<System::Byte>^ strBytes = Encoding::Unicode->GetBytes(value);

							for (int j = 0; j < strBytes->Length; j++)
							{
								buffer[offset + j] = strBytes[j];
							}

							offset += strBytes->Length;
						}

						break;
					case TdType::VarChar:
						{
							System::String^ value;

							if (currentObj->GetType() == array<System::Char>::typeid)
							{
								value = gcnew System::String((array<System::Char>^)currentObj);
							}
							else if (currentObj->GetType() == System::String::typeid)
							{
								value = (System::String^)currentObj;
							}
							else if (currentObj->GetType() == System::Char::typeid)
							{
								value = gcnew System::String(gcnew array<System::Char> { (System::Char)currentObj });
							}
							else
							{
								value = currentObj->ToString();
							}

							if (value->Length > sourceColumns[i]->Length)
							{
								value = value->Substring(0, sourceColumns[i]->Length);
							}

							array<System::Byte>^ strBytes = Encoding::Unicode->GetBytes(value);

							System::Array::Copy(System::BitConverter::GetBytes((System::UInt16)strBytes->Length), 0, buffer, offset, 2);
							offset += 2;

							for (int j = 0; j < strBytes->Length; j++)
							{
								buffer[offset + j] = strBytes[j];
							}

							offset += strBytes->Length;
						}

						break;
					case TdType::ByteInt:
						buffer[offset] = (unsigned char)System::Convert::ToByte(currentObj);
						offset += 1;

						break;
					case TdType::SmallInt:
						System::Array::Copy(System::BitConverter::GetBytes(System::Convert::ToInt16(currentObj)), 0, buffer, offset, 2);
						offset += 2;

						break;
					case TdType::Integer:
						System::Array::Copy(System::BitConverter::GetBytes(System::Convert::ToInt32(currentObj)), 0, buffer, offset, 4);
						offset += 4;

						break;
					case TdType::BigInt:
						System::Array::Copy(System::BitConverter::GetBytes(System::Convert::ToInt64(currentObj)), 0, buffer, offset, 8);
						offset += 8;

						break;
					case TdType::Decimal:
						{
							System::Decimal value = System::Convert::ToDecimal(currentObj);

							array<unsigned char>^ decBytes = gcnew array<unsigned char>(sourceColumns[i]->Size);

							array<System::Int32>^ decParts = System::Decimal::GetBits(value);

							int point = (decParts[3] >> 16) & 0xFF;

							if (point != sourceColumns[i]->Scale)
							{
								value = System::Decimal::Multiply(value, System::Convert::ToDecimal(System::Math::Pow(10, point - sourceColumns[i]->Scale)));
								decParts = System::Decimal::GetBits(value);
							}

							for (int j = 0; j < sourceColumns[i]->Size; j += 4)
							{
								if (j < 12)
								{
									array<unsigned char>^ partBytes = System::BitConverter::GetBytes(decParts[j / 4]);

									for (int k = 0; (j + k) < decBytes->Length && k < 4; k++)
									{
										decBytes[j + k] = partBytes[k];
									}
								}
								else
								{
									decBytes[j] = 0;
									decBytes[j + 1] = 0;
									decBytes[j + 2] = 0;
									decBytes[j + 3] = 0;
								}
							}

							// Negate
							if (decParts[3] < 0)
							{
								int carry = 1;

								// Twos Complement
								for (int j = 0; j < sourceColumns[i]->Size; j++)
								{
									if (carry == 1 && decBytes[j] > 0)
									{
										decBytes[j] = decBytes[j] - 1;
										carry = 0;
									}

									decBytes[j] = ~decBytes[j];
								}
							}

							System::Array::Copy(decBytes, 0, buffer, offset, sourceColumns[i]->Size);
							offset += sourceColumns[i]->Size;
						}

						break;
					case TdType::Double:
						System::Array::Copy(System::BitConverter::GetBytes(System::Convert::ToDouble(currentObj)), 0, buffer, offset, 8);
						offset += 8;

						break;
					case TdType::Date:
						{
							System::DateTime^ dt = System::Convert::ToDateTime(currentObj);
							int dtValue = (dt->Year - 1900) * 10000 + dt->Month * 100 + dt->Day;

							System::Array::Copy(System::BitConverter::GetBytes(dtValue), 0, buffer, offset, 4);
							offset += 4;
						}

						break;
					}
				}
			}

			char * ptr = UnMarshalArray(buffer, rowLen);

			BCCheckStatus(conn, conn->PutRow(ptr, rowLen));

			delete ptr;

			copied++;

			if (this->m_NotifyAfter > 0 && (copied % this->m_NotifyAfter) == 0)
			{
				System::Data::SqlClient::SqlRowsCopiedEventArgs^ e = gcnew System::Data::SqlClient::SqlRowsCopiedEventArgs(copied);
				this->RowsCopied(this, e);

				if (e->Abort || this->m_Abort)
				{
					break;
				}
			}
		}

		BCCheckStatus(conn, conn->EndAcquisition());

		BCCheckStatus(conn, conn->ApplyRows());
	}
	finally
	{
		try
		{
			conn->Terminate();
		}
		catch (System::Exception^)
		{
			// Squish
		}

		delete dmlGr;
		delete schema;
		delete conn;

		this->CleanUp();
	}
}

void
TPT::TdBulkCopy::CleanUp()
{

		Marshal::FreeHGlobal((System::IntPtr)this->username);
		Marshal::FreeHGlobal((System::IntPtr)this->password);
		
		Marshal::FreeHGlobal((System::IntPtr)this->server);
		Marshal::FreeHGlobal((System::IntPtr)this->database);
		Marshal::FreeHGlobal((System::IntPtr)this->target);
		Marshal::FreeHGlobal((System::IntPtr)this->log);

		if (logLevel != NULL)
		{
			Marshal::FreeHGlobal((System::IntPtr)this->logLevel);
		}

		Marshal::FreeHGlobal((System::IntPtr)this->insertStatement);

		for (int i = 0; i < TPT::TdBulkCopy::MaxColumns && this->fieldList[i] != NULL; i++)
		{
			Marshal::FreeHGlobal((System::IntPtr)this->fieldList[i]);
		}

		delete this->fieldList;
}
